#include "thermalcam.h"
#define MAXLINE 4096

static mutex mutexSocketSend;
    int socketSend(const int connfd, const string info);

std::string helloBeatInit(const string robotName);

std::string hearBeatInit(const string robotName);
void sendHeartBeat_thread(const int connfd);

void sendTempInfo_thread(const int connfd);
int sendDeviceTempInfo(const int connfd, const float tempMax, const float tempMin, const float tempAvg, const float diffTemp, const string tempImagePath);

void recvThread(const int connfd);
int sendTempAlarmInfo(const int connfd, const string imgPath,const string &imgStr, const float fx, const float fy);
string json2string(Json::Value json);
bool getWheatherExit();

int alarmNumber = 0;
//static Mat alarmImage_client;

static bool Thermal_linux_tcpStatus = false;
    std::mutex mutexThermal_linux_tcpStatus;
    bool getTLstatus();
    void setTLstatus(bool val);

static string ClientName = "Robot001"; 


void clientToLinux_thread(void *arg){
    while (1)
    {
        string selfName = "clientToLinux:: ";

        const string ip = ip_tcpToLinux;
        const int port = port_tcpToLinux;
        printf("clientToLinux.cpp:: ip: %s, port: %d\n", ip.c_str(), port);

    //  const string ip_tcpToLinux = "172.16.1.242";
    //  const int port_tcpToLinux = 8125;
        int  connfd;
        char  buff[MAXLINE];

        //ctreate socket for conmmunication
        connfd = socket(AF_INET, SOCK_STREAM, 0);
        if( connfd == -1 ){      //AF_INET: IPV4   ;SOCK_STREAM: TCP, //若成功则返回一个sockfd (套接字描述符)
            printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
            return ;
        }

        //connect service with ip and port
        struct sockaddr_in  servaddr;
        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        inet_pton(AF_INET, ip.c_str(), &servaddr.sin_addr.s_addr);
        servaddr.sin_port = htons(port);

        //reconnect 
        int count = 0;
        std::unique_lock<mutex> mylock_exit(mutexExitClient, std::try_to_lock);
        while(1){
            //exit 
            // if(mylock_exit.owns_lock() && bExitClient){
            //     break;
            // }
            if(getWheatherExit()){
                cout << "clientToLinux.cpp:: \033[1;32mclientToLinux()  exit\033[0m" << endl;
                close(connfd);
                return;
            }
            //<<<

            //connect
            int res = connect(connfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
            if(res != 0){
                printf("\rthermal-linux, connect: %d", count++);
                fflush(stdout);
                sleep(1);
                continue;
            }
        // printf("\nconnect success\n");

            //hello packet
            const string helloBeart_buf = "*****" + helloBeatInit("Robot001") + "#####";
            {
                //int res = send(connfd, helloBeart_buf.c_str(), strlen(helloBeart_buf.c_str() ) , 0);//向TCP连接的另一端发送数据。
                int res = 0;
                res = send(connfd, helloBeart_buf.c_str(), helloBeart_buf.size(), 0);

                char helloBeat_buf_recv[100];
                memset(helloBeat_buf_recv, 0 ,sizeof(helloBeat_buf_recv));
                int len = recv(connfd, helloBeat_buf_recv, sizeof(helloBeat_buf_recv), 0);

                if(len > 0){
                    printf("recive response \n %s \n", helloBeat_buf_recv);
                    Json::Value root;
                    if(string2Json(helloBeat_buf_recv, root) == 0 ){
                            if(root["HelloACK"].isNull()){
                                std::cerr << "HelloACK null\n";
                            }
                            setTLstatus(true);
                            Mconnfd = connfd;
                            printf("client connect hello successfully\n");
                    }
                }
                else if(len = 0){
                    printf("%s",selfName.c_str());
                    printf("connect break off \n");
                }
                else{
                    printf("%s",selfName.c_str());
                    printf("recv error\n");
                }
            }
            if(!getTLstatus()) {
                close(connfd);
                connfd = -1;
                printf("clientToLinux hello error\n");
                continue ;
            }
    //dgd
            //conmmunication  working
            int number = 0;
            thread heartBeat_t(sendHeartBeat_thread, connfd);
            thread recvThread_t(recvThread, connfd);
            thread sendTempInfo_t(sendTempInfo_thread, connfd);
            //thread sendThermalImage_t(sendThermalImage_thread, connfd);
        
            //exit heart
            printf("wait join hearbeat, recv and sendTempInfo threads\n");
            printf("\033[1;32mthermal communicate with linux start work\033[0m\n");
            if(heartBeat_t.joinable()){
                heartBeat_t.join();
                printf("clientToLinux.cpp:: \033[1;32mhearbeatThread exited \033[0m\n");
            }
            if(recvThread_t.joinable()){
                recvThread_t.join();
                printf("clientToLinux.cpp:: \033[1;32mrecvThread exited \033[0m\n");
            }
            if(sendTempInfo_t.joinable()){
                sendTempInfo_t.join();
                printf("clientToLinux.cpp:: \033[1;32msendTempInfoThread exited \033[0m\n");
            }
            // if(sendThermalImage_t.joinable()){
            //     sendThermalImage_t.join();
            // }

            printf("hearbeat, recv and sendTempInfo threads exit successfully\n");
            close(connfd);
            connfd = -1;
            break;

        }//while  reconnect

        //check wheather exit 
        // if(mylock_exit.owns_lock() && bExitClient){
        //     break;
        // }
        // else{
        //     printf("reconnect ......\n");
        //     close(connfd);
        //     continue;
        // }
        if(getWheatherExit()){
            close(connfd);
            break;
        }
        else{
            printf("reconnect ......\n");
            close(connfd);
            continue;
        }
        //<<<
    }//while

    
    cout << "clientToLinux.cpp:: \033[1;32mclientToLinux()  exit\033[0m" << endl;
    #undef MAXLINE
    return ;
}

int socketSend(const int connfd, const string info)
{
    unique_lock<mutex> lock(mutexSocketSend);
    return send(connfd, info.c_str(), info.size(), 0);
}

string json2string(Json::Value json)
{
    Json::StreamWriterBuilder writerBuilder;
    std::ostringstream os;
    
    std::unique_ptr<Json::StreamWriter> jsonWriter(writerBuilder.newStreamWriter());
    jsonWriter->write(json, &os);
    return os.str();
}
void sendHeartBeat_thread(const int connfd)
{
    Json::Value Hear;
    Json::StreamWriterBuilder writerBuilder;
    std::ostringstream os;
    
    Hear["Heartbeat"]["ID"] = "Robot001";
    Hear["Heartbeat"]["CurentTime"] = getLocalTime_self();

    int sendLen;
    while(getTLstatus()){
        std::unique_lock<mutex> mylock(mutexExitClient, std::try_to_lock);
        if(mylock.owns_lock() && bExitClient){
            cout << "clientToLinux.cpp:: sendHearbeat is exiting" << endl;
            break;
        }

        Hear["Heartbeat"]["CurentTime"] = getLocalTime_self();
        const string hearBeart_buf = "*****"+ json2string(Hear) + "#####";
        socketSend(connfd, hearBeart_buf);
        //printf("debug:connfd:%d  send len: %d\n",connfd, sendLen);
        this_thread::sleep_for(chrono::seconds(3));     // s
    }

    //printf ("clientToLinux.cpp:: sendHearbeart is exiting\n");
    return;
}
void sendTempInfo_thread(const int connfd)
{
    while(getTLstatus()){
        std::unique_lock<mutex> mylock(mutexExitClient, std::try_to_lock);
        if(mylock.owns_lock() && bExitClient){
            printf("clientToLinux.cpp:: sendTempInfo is exiting\n");
            break;
        }

        //tempInfo
        string thermalImgPath_inService = "null";

        mutexTempInfo.lock();
        if(isCapPicture){
            isCapPicture = false;
            thermalImgPath_inService = thermalImagePath_ftp;
        
            const float maxTemp = max_thermalTemp;
            const float minTemp = min_thermalTemp;
            const float avgTemp = avg_thermalTemp;
            const float diffTemp = diff_thermalTemp;
            mutexTempInfo.unlock();  
            
            sendDeviceTempInfo(connfd, maxTemp, minTemp, avgTemp, diffTemp, thermalImgPath_inService);
        }
        else{
            mutexTempInfo.unlock();
        }
        
        this_thread::sleep_for(chrono::seconds(1));
    }
}
void recvThread(const int connfd)
{
    Json::Value root;
    char buf_recv[1024];

    fd_set rfds;
    struct timeval tv;
    int retval, maxfd;
    
    int show_heartbeat_num = 0;
    while(1){
        std::unique_lock<mutex> mylock(mutexExitClient, std::try_to_lock);
        if(mylock.owns_lock() && bExitClient){
            printf("clientToLinux.cpp:: recvThread is exiting : %d\n " , bExitClient);
            //mutexExitClient.unlock();
            break;
        }

        //tempAlarm
        std::unique_lock<mutex> mylock2(mutexTempAlarm, std::try_to_lock);
        if(mylock2.owns_lock()){
            if(isTempAlarm){
                isTempAlarm = false;
                const string imgpath = TempAlarmImagePath;
                const float point_x = highPositionFx;
                const float point_y = highPositionFy;
                mylock2.unlock();  
                sendTempAlarmInfo(connfd, imgpath, "null", point_x, point_y);     
            }
            else{
                mylock2.unlock();
            }
        }

        /*把可读文件描述符的集合清空*/
        FD_ZERO(&rfds);
        /*把标准输入的文件描述符加入到集合中*/
        FD_SET(0, &rfds);
        maxfd = 0;
        /*把当前连接的文件描述符加入到集合中*/
        FD_SET(connfd, &rfds);
        /*找出文件描述符集合中最大的文件描述符*/
        if(maxfd < connfd)
            maxfd = connfd;
        /*设置超时时间*/
        tv.tv_sec = 5;
        tv.tv_usec = 0;

        /*等待聊天*/
        retval = select(maxfd+1, &rfds, NULL, NULL, &tv);
        if(retval == -1){
            printf("select出错，客户端程序退出\n");
            break;
        }
        else if(retval == 0){
            printf("客户端没有任何输入信息，并且服务器也没有信息到来，waiting...\n");
            continue;
        }
        else{
            /*服务器发来了消息*/
            if(FD_ISSET(connfd, &rfds)){
                memset(buf_recv, 0, sizeof(buf_recv));
                const int res = recv(connfd, buf_recv, sizeof(buf_recv),0);
                //printf("%s", buf_recv);

                if(res > 0){
                    if( string2Json(buf_recv, root) == -1){
                        printf("recv buff has a problems that cannot be parsed correctly\n");
                        break;
                    }
                    //1.helloBeat response
                    if(root.isMember("Heartbeat") ){ 
                        //std::cout << "this is heartBeat\n";
                        
                        const string info_heartbeat = "this is heartbeat " + to_string(++show_heartbeat_num);
                        printf("\r%s", info_heartbeat.c_str());
                        fflush(stdout);
                        continue;
                    }
                    if(root.isMember("DetectResponse") && root["DetectResponse"].isMember("Command")){
                        //cout << "recv tempAlarm response" << endl;
                        continue;
                    }

                    //cout <<"recvThread::  "<< buf_recv << endl;
                    printf("recv unknown\n\n");

                }
                else if(res ==0){
                    printf("service has break off\n");
                    break;
                }
                else{
                    perror("recv");
                    break;
                } 
            }// service send message
        }
    }//while

    setTLstatus(false);
    return;
}

int sendTempAlarmInfo(const int connfd, const string imgPath,const string &imgStr, const float fx, const float fy)
{
    std::string strjson;
    Json::Value DetectCommand, args,root;
    Json::StreamWriterBuilder writeBuild;
    std::ostringstream os;

    char timeBuf[50];
    getLocalTime_self(timeBuf, 50);

    args["ImageProcessMode"] = "TempAlarm";
    args["fx"] = to_string(fx);
    args["fy"] = to_string(fy);   
    args["Positions"] = 0;
    args["File_Path"] = imgPath; //"/home/ljl/catkin_tcp/socket_part/test/900005.png";     //linux service addr
    args["Time"] = timeBuf;
    root["DetectCommand"]["Args"] = Json::Value(args);
    root["DetectCommand"]["Command"] = "UpdatePic";

    std::unique_ptr<Json::StreamWriter> jsonWriter(writeBuild.newStreamWriter());
    jsonWriter->write(root,&os);
    strjson = "*****" + os.str() + "#####";
    socketSend(connfd, strjson);

    cout << "clientToLinux.cpp::sendTempAlarmInfo() "<<  ++alarmNumber << "  tempAlarm  has sended  imgPath:  " << imgPath << endl;
    return 0;
}
int sendDeviceTempInfo(const int connfd, const float tempMax, const float tempMin, const float tempAvg, const float diffTemp, const string tempImagePath)
{
    std::string strjson;
    Json::StreamWriterBuilder writeBuild;
    std::ostringstream os;

    // char timeBuf[50];
    // getLocalTime_self(timeBuf, 50);

    Json::Value root;
    root["TunnelTemperature"]["ID"] = ClientName;
    root["TunnelTemperature"]["Maximum"] = (int)tempMax;
    root["TunnelTemperature"]["Minimal"] = (int)tempMin;
    root["TunnelTemperature"]["Average"] = (int)tempAvg;
    root["TunnelTemperature"]["Diff"] = (int)tempAvg;
    root["TunnelTemperature"]["CurrentTime"] = getLocalTime_self();

    root["TunnelTemperature"]["CurrentPosition"] = (int)0;
    root["TunnelTemperature"]["DetailsSize"] = 0;
    root["TunnelTemperature"]["FileName"] = tempImagePath;

    std::unique_ptr<Json::StreamWriter> jsonWriter(writeBuild.newStreamWriter());
    jsonWriter->write(root,&os);
    strjson = "*****" + os.str() + "#####";
    socketSend(connfd, strjson);
    return 0;
}

std::string hearBeatInit(const string robotName)
{
    std::string jsonStr;
    Json::Value root, Hear;
    Json::StreamWriterBuilder writerBuilder;
    std::ostringstream os;
    
    char buf[50];
    memset(buf, 0, sizeof(buf));
    getLocalTime_self(buf, 50);

    root["ID"] = robotName;
    root["CurentTime"] = buf;
    Hear["Heartbeat"] = Json::Value(root);

    std::unique_ptr<Json::StreamWriter> jsonWriter(writerBuilder.newStreamWriter());
    jsonWriter->write(Hear, &os);
    jsonStr = os.str();
    return jsonStr;
}

std::string helloBeatInit(const string robotName){

    std::string jsonStr;
    Json::Value root, Hello;
    Json::StreamWriterBuilder writerBuilder;
    std::ostringstream os;

    char buf[50];
    memset(buf, 0, sizeof(buf));
    getLocalTime_self(buf,50);

    root["ID"] = robotName;
    root["TaskIdx"] = 1;
    root["TaskDescribe"] = "thermal_camera send temp alarm info";
    Hello["Hello"] = Json::Value(root);

    std::unique_ptr<Json::StreamWriter> jsonWriter(writerBuilder.newStreamWriter());
    jsonWriter->write(Hello, &os);
    jsonStr = os.str();

  //  jsonStr = root.toStyledString();
    return jsonStr;
}


int string2Json(const std::string &info, Json::Value &root)
{
    if (info.empty()) return -1;

    bool res;
    JSONCPP_STRING errs;
    //Json::Value root;
    Json::CharReaderBuilder readerBuilder;
    std::unique_ptr<Json::CharReader> const jsonReader(readerBuilder.newCharReader());
    //    reader.parse(jsonStr,root);       //for old version

    //string startMarkStr = "*****";
    //string endMarkStr = "#####";
    const int len = info.size();
    if(len <10){
        printf("string size %d < 10\n", len);
        return -1;
    }
    if(info.substr(0,5) == "*****" && info.substr(len-5,5 ) == "#####"){
        const string &jsonStr = info.substr(5, len-10);
        res = jsonReader->parse(jsonStr.c_str(), jsonStr.c_str()+jsonStr.length(), &root, &errs);        //new version return bool

        if (!res || !errs.empty()) {
            std::cout << " mString2Json :: parseJson err. " << errs << std::endl;
            return -1;
        }
    }
    else{
        printf("string start and end not ***** #####\n");
        return -1;
    }
    return 0;
}


bool getTLstatus()
{
    unique_lock<mutex> lock(mutexThermal_linux_tcpStatus);
    return Thermal_linux_tcpStatus;
}
void setTLstatus(bool val)
{
    unique_lock<mutex> lock(mutexThermal_linux_tcpStatus);
    Thermal_linux_tcpStatus = val;
}

bool getWheatherExit()
{
    std::unique_lock<mutex> mylock_exit(mutexExitClient, std::try_to_lock);
    //exit 
    if(mylock_exit.owns_lock() && bExitClient){
        return true;
    }
    return false;
    //<<<
}

